package com.sbtr.business.importmodel.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sbtr.business.auth.service.impl.RoleServiceImpl;
import com.sbtr.business.importmodel.entity.ImportModelTask;
import com.sbtr.business.importmodel.mapper.ImportModelTaskMapper;
import com.sbtr.business.importmodel.service.IImportModelTaskService;
import com.sbtr.business.publishtask.entity.PublishPredictTask;
import com.sbtr.business.publishtask.process.ServerClientProcessor;
import com.sbtr.business.publishtask.service.IPublishPredictTaskService;
import com.sbtr.business.trainingserver.service.IPublishServerHttpApi;
import com.sbtr.common.PageDto;
import com.sbtr.common.Response;
import com.sbtr.common.ResultCodeCanstant;
import com.sbtr.open.importmodel.dto.ImportModelTaskDto;
import com.sbtr.open.importmodel.dto.ImportModelTaskSearchDto;
import com.sbtr.open.publishtask.dto.PublishPredictTaskDto;
import com.sbtr.open.publishtask.dto.PublishTaskDataReqDto;
import com.sbtr.util.BeanCopyUtils;
import com.sbtr.util.PageDtoUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Transactional
@Service
public class ImportModelTaskServiceImpl extends ServiceImpl<ImportModelTaskMapper, ImportModelTask>
        implements IImportModelTaskService {

    private static final Logger logger = LoggerFactory.getLogger(ImportModelTaskServiceImpl.class);

    @Value("${oss.config.uploadPath}")
    private String uploadPath;

    @Autowired
    private IPublishPredictTaskService publishPredictTaskService;

    @Autowired
    private IPublishServerHttpApi publishServerHttpApi;

    @Autowired
    private RoleServiceImpl roleService;


    @Autowired
    private ServerClientProcessor serverClientProcessor;

    @Override
    public PageDto<ImportModelTaskDto> search(ImportModelTaskSearchDto pro) {
        IPage<ImportModelTaskDto> page = new Page<>();
        page.setSize(pro.getPageSize());
        page.setCurrent(pro.getPage());
        QueryWrapper<ImportModelTaskDto> wrapper = new QueryWrapper<>();
//		wrapper.eq(ColumnConstant.IS_AVAILABLE, 1);
//        wrapper.ne(ColumnConstant.PROJECT_TYPE, "data_unsupervised");

        ImportModelTaskDto dto = BeanCopyUtils.copyProperties(pro, ImportModelTaskDto.class);
        if (StringUtils.isNotEmpty(pro.getCreater())) {
            dto.setCreator(pro.getCreater());
        }

//        if (StringUtils.isNotBlank(pro.getName())) {
//            wrapper.like("name", pro.getName());
//        }
//
//        if (StringUtils.isNotEmpty(pro.getDescr())) {
//            wrapper.like("descr", pro.getDescr());
//        }
//
//        if( 0 != pro.getStatus()) {
//            wrapper.eq("status", pro.getStatus());
//        }
//
//        if (StringUtils.isNotEmpty(pro.getApi())) {
//            wrapper.like("api", pro.getApi());
//        }
//        if (StringUtils.isNotEmpty(pro.getCreater())) {
//            wrapper.eq("creator", pro.getCreater());
//        }

//        wrapper.orderByDesc("tm.id");
//		page = page(page, wrapper);
        page = baseMapper.customPage(page, dto);
        PageDto<ImportModelTaskDto> pageDto = PageDtoUtils.getPageDtoByIPage(page, ImportModelTaskDto.class);
        return pageDto;
    }

    @Override
    public ImportModelTaskDto getById(String id) {

        ImportModelTask task = this.getBaseMapper().selectById(id);
        return BeanCopyUtils.copyProperties(task, ImportModelTaskDto.class);
    }

    @Override
    public ImportModelTaskDto save(ImportModelTaskDto dto) {
        ImportModelTask task = BeanCopyUtils.copyProperties(dto, ImportModelTask.class);


        if (dto.getId() == null) {
            task.setCreateTime(new Date());
            task.setStatus(1);//已发布
        }
        this.saveOrUpdate(task);
        return BeanCopyUtils.copyProperties(task, ImportModelTaskDto.class);
    }

    @Override
    public Boolean delete(int id) {

        Boolean result = this.removeById(id);
        return result;
    }

    @Override
    public int batchDelete(List<Long> idList) {

        int result = this.getBaseMapper().deleteBatchIds(idList);
        return result;
    }

    @Override
    public ImportModelTaskDto upload(
            Long id,//任务id
            MultipartFile file) {

        if (file.isEmpty() || file.getSize() == 0) {
            return null;
        }
        try {

            ImportModelTask task = this.getById(id);
            String fileName = file.getOriginalFilename();//源文件名
            //防止上传重复文件报错，用UUID生成新的文件名
            String otherName = UUID.randomUUID().toString().replaceAll("-", "")
                    + fileName.substring(fileName.lastIndexOf("."));

            String bucketDir = uploadPath + File.separator + "importModel";
            String filePath = bucketDir + File.separator + otherName;

            OutputStream os = null;
            InputStream inputStream = null;
            try {
                inputStream = file.getInputStream();
                // 2、保存到临时文件
                // 1K的数据缓冲
                byte[] bs = new byte[1024];
                // 读取到的数据长度
                int len;
                // 输出的文件流保存到本地文件
                File tempFile = new File(bucketDir);
                boolean results = tempFile.mkdirs();
                if (!tempFile.exists()) {
                    tempFile.mkdirs();
                }
                os = new FileOutputStream(filePath);
                // 开始读取
                while ((len = inputStream.read(bs)) != -1) {
                    os.write(bs, 0, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 完毕，关闭所有链接
                try {
                    os.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            logger.info("上传外部模型成功,路径：{}", filePath);
            //更新task 路径：
            task.setFilePath(filePath);
            this.saveOrUpdate(task);
            return BeanCopyUtils.copyProperties(task, ImportModelTaskDto.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 导入模型的[测试按钮]，生成 预测任务
     * @param id
     * @param creater
     * @return
     */
/*    @RequestMapping(value = "/{id}/import-model-predict", method = RequestMethod.POST )
    public PublishPredictTaskDto predictDataMultiCreate(@PathVariable("id") Long id, @RequestParam(value = "predictFilePath") String predictFilePath,
                                                        @RequestParam("creater") String creater) {

        *//** 1.生成我的预测任务，跟踪预测流程 **//*
        //ImportModelTask task = this.getById(id);

        PublishPredictTask dto = new PublishPredictTask();
        dto.setCreater(creater);
        //dto.setPredictFile(bucketName+"/"+fileName);//+projectType+"/"+creater+"/"
        dto.setPublishId(id);
        dto.setTaskName("模型导入预测_"+task.getName());
        dto.setStatus(PublishPredictTaskDto.PREDICT_NOT_START);
        dto.setProjectId(task.getId());//导入的modelId
        dto.setCreateTime(new Date());
        dto.setPredictFile(predictFilePath);//上传的测试文件
        publishPredictTaskService.save(dto);

        */

    /**
     * 2.调用远程接口启动预测任务
     **//*
        //3.启动预测任务,远程调用预测接口
        return this.predictDataMulti(id,dto.getId());

        //return BeanCopyUtils.copyProperties(dto,PublishPredictTaskDto.class);
    }*/

    //	保存上传的测试文件
    public String saveTestFile(MultipartFile file, ImportModelTask task) {

        String fileName = file.getOriginalFilename();//源文件名
        //防止上传重复文件报错，用UUID生成新的文件名
        String otherName = UUID.randomUUID().toString().replaceAll("-", "")
                + fileName.substring(fileName.lastIndexOf("."));

        String bucketDir = uploadPath + File.separator + "importModelTest" + File.separator + task.getId();

        OutputStream os = null;
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            // 2、保存到临时文件
            // 1K的数据缓冲
            byte[] bs = new byte[1024];
            // 读取到的数据长度
            int len;
            // 输出的文件流保存到本地文件
            File tempFile = new File(bucketDir);
            boolean results = tempFile.mkdirs();
            if (!tempFile.exists()) {
                tempFile.mkdirs();
            }
            os = new FileOutputStream(tempFile.getPath() + File.separator + otherName);
            // 开始读取
            while ((len = inputStream.read(bs)) != -1) {
                os.write(bs, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 完毕，关闭所有链接
            try {
                os.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bucketDir + File.separator + otherName;
    }


    /**
     * 发布外部模型:
     * 发送请求到python端
     *
     * @param req
     * @return
     */
    @Override
    public Response<String> publishExternalModel(PublishTaskDataReqDto req) {

        // 导入的模型是否已经存在
        ImportModelTask task = this.getById(req.getModelId());
        Assert.isTrue(task != null, "模型不存在,模型id [" + req.getModelId() + "]");

        logger.info("处理模型发布start | req: {}", JSON.toJSONString(req));
        Long modelId = req.getModelId();
        if (null == modelId || modelId < 1) {
            logger.error("模型id参数不合法 | modelId: {}", modelId);
            return Response.error(ResultCodeCanstant.PARAMETER_ERROR.getValue(), "模型id参数不合法");
        }

        String result = null;
        try {
            //1.调用python端模型发布接口
            result = serverClientProcessor.publishExternalModel(task);
            if (StringUtils.isNotBlank(result)) {
                JSONObject re = JSONObject.parseObject(result);
                if (re.getInteger("code") == 200) {
                    logger.info(re.getString("data") + ",modelId={}", modelId);
                    // 2.更改任务状态
                    task.setStatus(2);//已发布
                    //task.setUpdateTime(new Date());
                    this.saveOrUpdate(task);
                    return Response.success(result);
                } else {
                    logger.error("发布模型接口调用失败，msg:{}", result);
                }
            } else {
                logger.error("发布模型接口调用失败，msg:{}", result);
            }


            return Response.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "发布模型接口调用失败");
        } catch (Exception e) {
            e.printStackTrace();
            //return Response.error(result);
            return Response.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "发布模型接口调用失败" + e.getMessage());
        }
    }

    /**
     * 下线外部模型:
     * 发送请求到python端
     *
     * @param req
     * @return
     */
    @Override
    public Response<Boolean> offlineExternalModel(PublishTaskDataReqDto req) {

        // 导入的模型是否已经存在
        ImportModelTask task = this.getById(req.getModelId());
        Assert.isTrue(task != null, "模型不存在,模型id [" + req.getModelId() + "]");

        logger.info("处理模型发布start | req: {}", JSON.toJSONString(req));
        Long modelId = req.getModelId();
        if (null == modelId || modelId < 1) {
            logger.error("模型id参数不合法 | modelId: {}", modelId);
            return Response.error(ResultCodeCanstant.PARAMETER_ERROR.getValue(), "模型id参数不合法");
        }

        Boolean result = null;
        try {
            //1.调用python端模型发布接口
            result = serverClientProcessor.offLineExternalModel(task);
            if (result) {
                logger.info("处理下线模型success | modelId: {}", modelId);

                // 2.更改任务状态
                task.setStatus(3);//已下线
                //task.setUpdateTime(new Date());
                this.saveOrUpdate(task);
                return Response.success(result);
            } else {
                return Response.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "发布模型接口调用失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return Response.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "发布模型接口调用失败");
        }
    }

    /**
     * 新建预测任务，保存预测文件
     * @param req
     * @return
     */
 /*   @RequestMapping(value = "/{id}/predict-data-multi-create", method = RequestMethod.POST)
    @Deprecated
    public PublishPredictTaskDto predictDataMultiCreate(@PathVariable("id") Long id,@RequestBody PredictDataMultiNewDto req) {

        String creater = req.getCreater();
        String fileName = req.getFileOssName();

        String bucketName=req.getBucketDir();
        // 先处理上传的文件
        if (!fileName.toLowerCase().endsWith(".xls") && !fileName.toLowerCase().endsWith(".xlsx")
                && !fileName.toLowerCase().endsWith(".txt") && !fileName.toLowerCase().endsWith(".csv")
                && !fileName.toLowerCase().endsWith(".zip")) {
            logger.error(fileName + " 上传文件不是excel,csv,txt,zip");
            throw new RuntimeException(fileName + " 上传文件不是excel,csv,txt,zip");
        }

        PublishPredictTask dto = new PublishPredictTask();

        Assert.isTrue(StringUtils.isNotBlank(creater), "用户账号不能为空");
        //PublishTaskDataDto publishTaskDataDto = this.findById(id);
        ImportModelTask task = this.getById(id);


        // 更改项目状态
        //Project project = projectService.getById(publishTaskDataDto.getProjectId());
        //String projectType = project.getProjectType();

        dto.setCreater(creater);
        dto.setPredictFile(bucketName+"/"+fileName);//+projectType+"/"+creater+"/"
        dto.setPublishId(id);//Import_model_task 的id
        dto.setTaskName(task.getName()+"_数据预测_"+task.getCreator()+"_"+System.currentTimeMillis());

        dto.setStatus(PublishPredictTaskDto.PREDICT_NOT_START);
        dto.setProjectId(id);//导入模型的id
        dto.setCreateTime(new Date());
        //2.新建预测任务
        publishPredictTaskService.save(dto);

        //3.启动预测任务,远程调用预测接口
        this.predictDataMulti(id,dto.getId());

        return BeanCopyUtils.copyProperties(dto,PublishPredictTaskDto.class);
    }*/

    /**
     * 启动预测任务,远程调用预测接口
     *
     * @param modelId       importModelTask.id
     * @param predictTaskId 预测任务id
     * @return
     */
    public PublishPredictTaskDto predictDataMulti(Long modelId, Long predictTaskId) {
        // TODO Auto-generated method stub
        PublishPredictTask task = publishPredictTaskService.getById(predictTaskId);
        //Long publishId = task.getPublishId();//null
        task.setStatus(PublishPredictTaskDto.PREDICT_RUNNING);
        task.setStartTime(new Date());
        publishPredictTaskService.saveOrUpdate(task);//更改状态：正在预测

        //TrainingServerDto serverDto = null;

        String api = publishServerHttpApi.predictData(modelId, predictTaskId);
        ImportModelTask modelTask = this.getById(modelId);
        modelTask.setApi(api);//保存api
        this.saveOrUpdate(modelTask);

        return BeanCopyUtils.copyProperties(task, PublishPredictTaskDto.class);
    }
}
